home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Tech Arsenal 1
/
Tech Arsenal (Arsenal Computer).ISO
/
tek-19
/
iritsm3s.zip
/
ADJACNCY.C
< prev
next >
Wrap
C/C++ Source or Header
|
1992-01-11
|
21KB
|
523 lines
/*****************************************************************************
* "Irit" - the 3d polygonal solid modeller. *
* *
* Written by: Gershon Elber Ver 0.2, Mar. 1990 *
******************************************************************************
* Module to handle adjacancies between polygons. Each edge has exactly two *
* polygons which share it. An edge is implicitly defined by the VList - each *
* VertexStruct defines an edge with its succesor, and has a pointer to the *
* other polygons using that edge. Those pointers are our target in this *
* module. *
*****************************************************************************/
#include <stdio.h>
#include <math.h>
#include "program.h"
#include "adjacncy.h"
#include "allocate.h"
/* #define DEBUG If the adjacencies should be printed to stdout. */
/* #define DEBUG2 If the hash table content should be printed to stdout. */
#define HASH_TABLE_SIZE 100
#define HASH_TABLE_SIZE1 101 /* One above the above. */
#define HASH_TABLE_SIZE2 50 /* Half of the above. */
typedef struct HashTableEntry {
int Key;
PolygonStruct *Pl;
VertexStruct *V;
struct HashTableEntry *Pnext;
} HashTableEntry;
typedef struct HashTableStruct {
HashTableEntry *Entry[HASH_TABLE_SIZE1];
} HashTableStruct;
/* Prototypes of local function of adjacecies module: */
static void InsertHashTable(HashTableStruct *HashTbl, PolygonStruct *Pl,
VertexStruct *V);
static int EdgeKey(VertexStruct *V);
static HashTableEntry *FindMatchEdge(HashTableStruct *HashTbl, int EntryNum,
HashTableEntry *PHash);
static int SameEdges(PointType V1E1, PointType V2E1,
PointType V1E2, PointType V2E2);
static void InsertSecondHashTable(HashTableStruct *SecondHashTbl,
HashTableEntry *PHash);
static void SecondEdgeKey(VertexStruct *V, int *Key1, int *Key2);
static HashTableEntry *FindSecondMatchEdge(HashTableStruct *SecondHashTbl,
int EntryNum, HashTableEntry *PHash);
static int TestSameDir(PointType Pt11, PointType Pt12,
PointType Pt21, PointType Pt22);
static void DeleteHashTable(HashTableStruct *SecondHashTable,
VertexStruct *V, int EntryNum);
/*****************************************************************************
* Routine to generate adjacencies to the given object. Returns TRUE if all *
* adjacencies were resolved, meaning the object is perfectly closed. *
* Note an edge might be only partially adjacent to another edge, and a *
* second attempt is made to find (again only part of - see below) them. Any *
* case, FALSE will be returned as there is no way we can say the object is *
* perfectly closed! *
* This is the only routine to generate the adjacencies of a geometric *
* object. These adjacencies are needed for the boolean operations on them. *
* Algorithm: for each edge, for each polygon in the object, the edges are *
* sorted according to the key defined by EdgeKey routine (sort in hash tbl). *
* A second path on the table is made to match common keys edges and set the *
* pointers from one to another. Note that each edge is common to exactly 2 *
* faces if it is internal, or exactly 1 face if it is on the border (if the *
* object is open). *
*****************************************************************************/
int GenAdjacencies(ObjectStruct *PObj)
{
int i, IsOpenObject;
HashTableStruct *HashTbl, *SecondHashTbl;
HashTableEntry *PHash, *PHashMatch;
PolygonStruct *Pl;
VertexStruct *V;
if (!IS_POLY_OBJ(PObj))
FatalError("GenAdjacencies: Non polygonal object");
if (IS_POLYLINE_OBJ(PObj)) return TRUE; /* No adj. in polyline obj. */
IsOpenObject = FALSE; /* "Default" is closed object... */
/* Prepare hash tables (for first and second attempts) and clear them. */
HashTbl = (HashTableStruct *) MyMalloc(sizeof(HashTableStruct),
ALLOC_OTHER);
for (i = 0; i < HASH_TABLE_SIZE1; i++) HashTbl -> Entry[i] = NULL;
SecondHashTbl = (HashTableStruct *) MyMalloc(sizeof(HashTableStruct),
ALLOC_OTHER);
for (i = 0; i < HASH_TABLE_SIZE1; i++) SecondHashTbl -> Entry[i] = NULL;
/* Step one - enter all the edges into the hash table: */
Pl = PObj -> U.Pl.P;
while (Pl) {
V = Pl -> V;
do {
InsertHashTable(HashTbl, Pl, V); /* Insert the edge V..V->Pnext. */
V = V -> Pnext;
} while (V != NULL && V != Pl -> V);
Pl = Pl -> Pnext;
}
#ifdef DEBUG2
printf("Hash Table content:\n");
for (i = 0; i < HASH_TABLE_SIZE; i++) {
PHash = HashTbl -> Entry[i];
if (PHash) printf("\nHashTable entry %d:\n", i);
while (PHash) {
printf("Edge %10lf %10lf %10lf :: %10lf %10lf %10lf\n",
PHash -> V -> Pt[0],
PHash -> V -> Pt[1],
PHash -> V -> Pt[2],
PHash -> V -> Pnext -> Pt[0],
PHash -> V -> Pnext -> Pt[1],
PHash -> V -> Pnext -> Pt[2]);
PHash = PHash -> Pnext;
}
}
#endif /* DEBUG2 */
/* Step two - scans all the entries and look for the matching edge. */
for (i = 0; i < HASH_TABLE_SIZE; i++)
while (HashTbl -> Entry[i] != NULL) {
PHash = HashTbl -> Entry[i]; /* Remove one edge from hash table. */
HashTbl -> Entry[i] = HashTbl -> Entry[i] -> Pnext;
/* Find matching edge (if perfect match - exactly the same edge) */
/* Otherwise put the edge in SecondHashTbl. */
if ((PHashMatch = FindMatchEdge(HashTbl, i, PHash)) == NULL) {
PHash -> V -> PAdj = NULL;
InsertSecondHashTable(SecondHashTbl, PHash);
IsOpenObject = TRUE;
}
else {
# ifdef DEBUG
/* If debug switch the pointers of the edges themselves. */
PHash -> V -> PAdj = (PolygonStruct *) PHashMatch -> V;
PHashMatch -> V -> PAdj = (PolygonStruct *) PHash -> V;
# else
/* Otherwise switch pointers of the edges polygons */
PHash -> V -> PAdj = PHashMatch -> Pl;
PHashMatch -> V -> PAdj = PHash -> Pl;
# endif /* DEBUG */
MyFree((char *) PHash, ALLOC_OTHER);
MyFree((char *) PHashMatch, ALLOC_OTHER);
}
}
#ifdef DEBUG
printf("Adjacencies for object %s (found to be open = %d)\n",
PObj -> Name, IsOpenObject);
Pl = PObj -> U.Pl.P;
/* Note that the adjacency in DEBUG is the other edge, not other polygon.*/
while (Pl) {
V = Pl -> V;
do {
printf("Edge %10lf %10lf %10lf :: %10lf %10lf %10lf\n",
V -> Pt[0], V -> Pt[1], V -> Pt[2],
V -> Pnext -> Pt[0], V -> Pnext -> Pt[1], V -> Pnext -> Pt[2]);
if (V -> PAdj != NULL)
printf("Match %10lf %10lf %10lf :: %10lf %10lf %10lf\n\n",
((VertexStruct *) V -> PAdj) -> Pt[0],
((VertexStruct *) V -> PAdj) -> Pt[1],
((VertexStruct *) V -> PAdj) -> Pt[2],
((VertexStruct *) V -> PAdj) -> Pnext -> Pt[0],
((VertexStruct *) V -> PAdj) -> Pnext -> Pt[1],
((VertexStruct *) V -> PAdj) -> Pnext -> Pt[2]);
else
printf("No Match!\n\n");
V = V -> Pnext;
} while (V != NULL && V != Pl -> V);
Pl = Pl -> Pnext;
}
#endif /* DEBUG */
#ifdef DEBUG2
printf("Hash Table content left after all full matches were deleted:\n");
for (i = 0; i < HASH_TABLE_SIZE; i++) {
PHash = SecondHashTbl -> Entry[i];
if (PHash) printf("\nHashTable entry %d:\n", i);
while (PHash) {
printf("Edge %10lf %10lf %10lf :: %10lf %10lf %10lf\n",
PHash -> V -> Pt[0],
PHash -> V -> Pt[1],
PHash -> V -> Pt[2],
PHash -> V -> Pnext -> Pt[0],
PHash -> V -> Pnext -> Pt[1],
PHash -> V -> Pnext -> Pt[2]);
PHash = PHash -> Pnext;
}
}
#endif /* DEBUG2 */
/* Time to activate the second attempt - scan SecondHashTable for edges */
/* partially adjacent to each other, but with one common vertex! */
for (i = 0; i < HASH_TABLE_SIZE; i++)
while (SecondHashTbl -> Entry[i] != NULL) {
PHash = SecondHashTbl -> Entry[i];/* Remove one edge from table. */
SecondHashTbl -> Entry[i] = SecondHashTbl -> Entry[i] -> Pnext;
/* Remove the second instance of this edge with other key: */
DeleteHashTable(SecondHashTbl, PHash -> V, PHash -> Key);
/* Find matching edge (if perfect match - exactly the same edge) */
/* Otherwise put the edge in SecondHashTbl. */
if ((PHashMatch = FindSecondMatchEdge(SecondHashTbl, i, PHash)) ==
NULL) {
PHash -> V -> PAdj = NULL; /* Failed again! */
MyFree((char *) PHash, ALLOC_OTHER);
}
else {
# ifdef DEBUG
/* If debug switch the pointers of the edges themselves. */
PHash -> V -> PAdj = (PolygonStruct *) PHashMatch -> V;
PHashMatch -> V -> PAdj = (PolygonStruct *) PHash -> V;
# else
/* Otherwise switch pointers of the edges polygons. */
PHash -> V -> PAdj = PHashMatch -> Pl;
PHashMatch -> V -> PAdj = PHash -> Pl;
# endif /* DEBUG */
MyFree((char *) PHash, ALLOC_OTHER);
MyFree((char *) PHashMatch, ALLOC_OTHER);
}
}
#ifdef DEBUG
printf("Adjacencies for object %s - second attempt (found to be open = %d)\n",
PObj -> Name, IsOpenObject);
Pl = PObj -> U.Pl.P;
/* Note that the adjacency in DEBUG is the other edge, not other polygon.*/
while (Pl) {
V = Pl -> V;
do {
printf("Edge %10lf %10lf %10lf :: %10lf %10lf %10lf\n",
V -> Pt[0], V -> Pt[1], V -> Pt[2],
V -> Pnext -> Pt[0], V -> Pnext -> Pt[1], V -> Pnext -> Pt[2]);
if (V -> PAdj != NULL)
printf("Match %10lf %10lf %10lf :: %10lf %10lf %10lf\n\n",
((VertexStruct *) V -> PAdj) -> Pt[0],
((VertexStruct *) V -> PAdj) -> Pt[1],
((VertexStruct *) V -> PAdj) -> Pt[2],
((VertexStruct *) V -> PAdj) -> Pnext -> Pt[0],
((VertexStruct *) V -> PAdj) -> Pnext -> Pt[1],
((VertexStruct *) V -> PAdj) -> Pnext -> Pt[2]);
else
printf("No Match!\n\n");
V = V -> Pnext;
} while (V != NULL && V != Pl -> V);
Pl = Pl -> Pnext;
}
#endif /* DEBUG */
MyFree((char *) HashTbl, ALLOC_OTHER);
MyFree((char *) SecondHashTbl, ALLOC_OTHER);
return !IsOpenObject;
}
/*****************************************************************************
* Evaluate a key (integer!) from the given vertex V (in polygon Pl) and *
* insert that in the hash table: *
*****************************************************************************/
static void InsertHashTable(HashTableStruct *HashTbl, PolygonStruct *Pl,
VertexStruct *V)
{
int Key;
HashTableEntry *PHash;
PHash = (HashTableEntry *) MyMalloc(sizeof(HashTableEntry), ALLOC_OTHER);
PHash -> Pl = Pl;
PHash -> V = V;
PHash -> Key = Key = EdgeKey(V);
PHash -> Pnext = HashTbl -> Entry[Key];
HashTbl -> Entry[Key] = PHash;
}
/*****************************************************************************
* This routine evaluate a key for the given edge. In order the try to make *
* them unique as possible, the point is projected on a "random" vector. I *
* picked vector X + 1.57 * Y + 1.29 * Z. If you have better one - change it. *
* The key itself is the average of the two vertices keys. *
* Note we get best results if the object is between ~-10..10. *
*****************************************************************************/
static int EdgeKey(VertexStruct *V)
{
int key;
RealType RKey1, RKey2;
RKey1 = (V -> Pt[0] + 1.57 * V -> Pt[1] + 1.29 * V -> Pt[2]);
V = V -> Pnext;
RKey2 = (V -> Pt[0] + 1.57 * V -> Pt[1] + 1.29 * V -> Pt[2]);
key = (((int) ((RKey1 + RKey2) * 10.0)) + HASH_TABLE_SIZE) / 2;
return BOUND(key, 0, HASH_TABLE_SIZE - 1);
}
/*****************************************************************************
* Search The hash table for matching with the given edge pointed by PHash. *
* PHash was extracted from the hash table in entry EntryNum, so the match *
* is probably in the same entry. If it is not, it must be (if there is one!) *
* in EntryNum+1 as we scans the entries in order and (EntryNum-1) is empty. *
* Note that idealy the match was in EntryNum, but because of real number *
* errors there is a small chance it will be in its neibours: EntryNum +/- 1. *
*****************************************************************************/
static HashTableEntry *FindMatchEdge(HashTableStruct *HashTbl, int EntryNum,
HashTableEntry *PHash)
{
int i;
HashTableEntry *PLast = NULL, *PMatch;
for (i = EntryNum; i <= EntryNum+1; i++) {
PMatch = HashTbl -> Entry[i];
while (PMatch) {
if (SameEdges(PHash -> V -> Pt, PHash -> V -> Pnext -> Pt,
PMatch -> V -> Pt, PMatch -> V -> Pnext -> Pt)) {
/* Delete the matched edge from hash table, and return it: */
if (PMatch == HashTbl -> Entry[i])
HashTbl -> Entry[i] = HashTbl -> Entry[i] -> Pnext;
else
PLast -> Pnext = PLast -> Pnext -> Pnext;
return PMatch;
}
PLast = PMatch;
PMatch = PMatch -> Pnext;
}
}
return NULL; /* No match for this one ! */
}
/*****************************************************************************
* Compere two edges - if the same up to an EPSILON (see APX_EQ, irit.h). *
* The two vetrices of each edge are given, but no order on them is assumed *
*****************************************************************************/
static int SameEdges(PointType V1E1, PointType V2E1,
PointType V1E2, PointType V2E2)
{
return (PT_EQ(V1E1, V1E2) && PT_EQ(V2E1, V2E2)) ||
(PT_EQ(V1E1, V2E2) && PT_EQ(V2E1, V1E2));
}
/******************************************************************************
* Everything from this point handle the second attempt - try to match edges *
* which are not complete match - cases which one edge is only part of its *
* adjacent one. We trap only cases which the two edges has common vertex. If *
* the two edges has no common vertex (i.e. one is totally in the other) we *
* still misses that. You are invited to improve that. Any case this one will *
* have influence in extremely rare cases (The booleans will usually propagate *
* the information using the common vertex edges). *
* Note, the obvious, that if one edge is adjacent to few edges, only one *
* (arbitrarily) will result in the match, and the other will result as NULL. *
******************************************************************************/
/*****************************************************************************
* Evaluate two keys (integer!) from the given edge in HashTableEntry struct. *
* This time the keys are of the vertices themselves (see SecondEdgeKet rtn). *
* Note each HashTableEntry hold the key of the other entry this time... *
*****************************************************************************/
static void InsertSecondHashTable(HashTableStruct *SecondHashTbl,
HashTableEntry *PHash)
{
int Key1, Key2;
HashTableEntry *PHash2;
SecondEdgeKey(PHash -> V, &Key1, &Key2);
/* And insert the edge as at Key1 (using given HashTableEntry PHash): */
PHash -> Key = Key2;
PHash -> Pnext = SecondHashTbl -> Entry[Key1];
SecondHashTbl -> Entry[Key1] = PHash;
/* And insert the edge as at Key2 (allocating new HashTableEntry for it):*/
PHash2 = (HashTableEntry *) MyMalloc(sizeof(HashTableEntry), ALLOC_OTHER);
PHash2 -> Pl = PHash -> Pl;
PHash2 -> V = PHash -> V;
PHash2 -> Key = Key1;
PHash2 -> Pnext = SecondHashTbl -> Entry[Key2];
SecondHashTbl -> Entry[Key2] = PHash2;
}
/*****************************************************************************
* This routine evaluate two keys for the given edge - one for each of its *
* vertices, and again tries to make the unique as passible: *
* picked the same vector: X + 1.57 * Y + 1.29 * Z. *
* Note we get best results if the object is between ~-10..10. *
*****************************************************************************/
static void SecondEdgeKey(VertexStruct *V, int *Key1, int *Key2)
{
RealType RKey;
RKey = (V -> Pt[0] + 1.57 * V -> Pt[1] + 1.29 * V -> Pt[2]);
*Key1 = ((int) (RKey * 10.0) + HASH_TABLE_SIZE) / 2;
*Key1 = BOUND(*Key1, 0, HASH_TABLE_SIZE - 1);
V = V -> Pnext;
RKey = (V -> Pt[0] + 1.57 * V -> Pt[1] + 1.29 * V -> Pt[2]);
*Key2 = ((int) (RKey * 10.0) + HASH_TABLE_SIZE) / 2;
*Key2 = BOUND(*Key2, 0, HASH_TABLE_SIZE - 1);
}
/*****************************************************************************
* Search The hash table for matching with the given edge pointed by PHash, *
* as in the second attempt: the keys used here are of the vertices *
* themselves, so we should search for match in given index EntryNum only. *
* We search for same vertex AND same direction, which if both match, confirm *
* at list partial adjacency between the two edges (both with same vertex as *
* one end - the vertex with this key). *
*****************************************************************************/
static HashTableEntry *FindSecondMatchEdge(HashTableStruct *SecondHashTbl,
int EntryNum, HashTableEntry *PHash)
{
int EqualFirst, SameDir = FALSE;
HashTableEntry *PLast = NULL, *PMatch;
PMatch = SecondHashTbl -> Entry[EntryNum]; /* It must be here if exists. */
while (PMatch) {
if ((EqualFirst = PT_EQ(PHash -> V -> Pt, PMatch -> V -> Pt)) != 0
|| PT_EQ(PHash -> V -> Pt, PMatch -> V -> Pnext -> Pt)) {
/* Found same vertex in PMatch as first vertex in PHash - test */
/* the direction vectors, to be same also: */
if (EqualFirst) {
SameDir = TestSameDir(PHash -> V -> Pnext -> Pt,
PHash -> V -> Pt,
PMatch -> V -> Pnext -> Pt,
PMatch -> V -> Pt);
}
else {
SameDir = TestSameDir(PHash -> V -> Pnext -> Pt,
PHash -> V -> Pt,
PMatch -> V -> Pt,
PMatch -> V -> Pnext -> Pt);
}
}
else if ((EqualFirst = PT_EQ(PHash -> V -> Pnext -> Pt,
PMatch -> V -> Pt)) != 0 ||
PT_EQ(PHash -> V -> Pnext -> Pt, PMatch -> V -> Pnext -> Pt)) {
/* Found same vertex in PMatch as second vertex in PHash - test */
/* the direction vectors, to be same also: */
if (EqualFirst) {
SameDir = TestSameDir(PHash -> V -> Pt,
PHash -> V -> Pnext -> Pt,
PMatch -> V -> Pnext -> Pt,
PMatch -> V -> Pt);
}
else {
SameDir = TestSameDir(PHash -> V -> Pt,
PHash -> V -> Pnext -> Pt,
PMatch -> V -> Pt,
PMatch -> V -> Pnext -> Pt);
}
}
if (SameDir) { /* TRUE iff same vertex AND same direction!!! */
/* Delete the matched edge from the hash table, its compliment */
/* with the second key and return a pointer to it: */
if (PMatch == SecondHashTbl -> Entry[EntryNum])
SecondHashTbl -> Entry[EntryNum] =
SecondHashTbl -> Entry[EntryNum] -> Pnext;
else
PLast -> Pnext = PLast -> Pnext -> Pnext;
/* Uses the key in structure (hold key of other entry!): */
DeleteHashTable(SecondHashTbl, PMatch -> V, PMatch -> Key);
return PMatch;
}
PLast = PMatch;
PMatch = PMatch -> Pnext;
}
return NULL; /* No match for this one ! */
}
/*****************************************************************************
* Test the the two point pairs (defined two edges) are actually on the *
* same direction - find normalized direction vector for each and test if *
* their dot product is equal to 1. *
*****************************************************************************/
static int TestSameDir(PointType Pt11, PointType Pt12,
PointType Pt21, PointType Pt22)
{
PointType Dir1, Dir2;
PT_SUB(Dir1, Pt12, Pt11);
PT_SUB(Dir2, Pt22, Pt21);
PT_NORMALIZE(Dir1);
PT_NORMALIZE(Dir2);
return APX_EQ(DOT_PROD(Dir1, Dir2), 1.0);
}
/*****************************************************************************
* Delete entry in SecondHashTable index EntryNum, which holds vertex V. *
* This vertex MUST be there, otherwise its a fatal error. *
*****************************************************************************/
static void DeleteHashTable(HashTableStruct *SecondHashTable,
VertexStruct *V, int EntryNum)
{
HashTableEntry *PLast, *PHash = SecondHashTable -> Entry[EntryNum];
while (PHash != NULL) {
if (PHash -> V == V) break;
PLast = PHash;
PHash = PHash -> Pnext;
}
if (PHash == NULL)
FatalError("DeleteHashTable: No hash table entry to delete\n");
else {
if (PHash == SecondHashTable -> Entry[EntryNum])
SecondHashTable -> Entry[EntryNum] =
SecondHashTable -> Entry[EntryNum] -> Pnext;
else
PLast -> Pnext = PHash -> Pnext;
MyFree((char *) PHash, ALLOC_OTHER);
}
}